package com.aifast.common.forUpdate;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.lang.Console;
import cn.hutool.core.text.StrBuilder;
import cn.hutool.core.util.ClassUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.spring.SpringUtil;
import cn.hutool.json.JSON;
import cn.hutool.json.JSONUtil;
import com.aifast.common.cache.util.RedisUtil;
import com.aifast.common.global.Const;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.core.toolkit.PluginUtils;
import com.baomidou.mybatisplus.extension.plugins.inner.InnerInterceptor;
import com.jfinal.plugin.activerecord.Db;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlCommandType;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import sun.reflect.misc.FieldUtil;

import javax.annotation.Resource;
import java.lang.reflect.Field;
import java.sql.SQLException;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Objects;

/**
 *
 * ForUpdate插件 <br/>主要解决 【谁】在【什么时间】修改了【什么数据】的内容的【前后差异】
 *
 * @Author fujian
 * @since 2022/11/19 16:11
 */
@Component
@Slf4j
@SuppressWarnings({"rawtypes"})
public class ForUpdateInnerInterceptor implements InnerInterceptor {

    private static final List<String> exclusion = Arrays.asList("log_");

    private static IForUpdateService service;

    @Autowired
    public void setIForUpdateService(IForUpdateService iForUpdateService){
        ForUpdateInnerInterceptor.service = iForUpdateService;
    }

    @Override
    public void beforeUpdate(Executor executor, MappedStatement ms, Object parameter) throws SQLException {
        if(!ms.getSqlCommandType().equals(SqlCommandType.UPDATE))return;
        String sql = ms.getBoundSql(parameter).getSql();
        if(exclusion.stream().anyMatch(sql::contains))return;

        String[] strings = ms.getId().split(Const.SYMBOL_SPOT);
        String str = strings[strings.length - 2];
        if(StrUtil.isBlank(str)){
            return;
        }
        String tmpBeanName = str.substring(0,1).toLowerCase() + str.substring(1);
        Object bean = SpringUtil.getBean(tmpBeanName);
        Map<String, Object> map = Convert.toMap(String.class, Object.class, parameter);
        Object param1 = map.get("param1");
        JSON parse = JSONUtil.parse(param1);
        Object id = parse.getByPath("id");
        Object invoke = ReflectUtil.invoke(bean, "selectById", id);
        StrBuilder builder = StrBuilder.create();
        Field[] oldFields = invoke.getClass().getDeclaredFields();
        Field[] newFields = param1.getClass().getDeclaredFields();
        TableName tableName = invoke.getClass().getAnnotation(TableName.class);
        String tmpTableName = ObjectUtil.isNotNull(tableName) ? tableName.value() : StrUtil.toUnderlineCase(invoke.getClass().getSimpleName());
        String join = StrUtil.join(Const.SYMBOL_COLON, "AiFast", "forUpdate", tmpTableName);
        JSON json = JSONUtil.parse(RedisUtil.get(join));
        for(Field field : oldFields) {
            field.setAccessible(true);
            try {
                Object newValue = field.get(param1);
                Object oldValue = field.get(invoke);
                if(ObjectUtil.isNull(newValue) )continue;
                if(!Objects.equals(newValue, oldValue) && newValue != oldValue) {
                    builder.append(RedisUtil.exists(join) ? json.getByPath(field.getName()) : field.getName());
                    builder.append(": 【更改前：");
                    builder.append(oldValue);
                    builder.append(", 更改后：");
                    builder.append(newValue);
                    builder.append("】;");
                }

            } catch (Exception e) {
                Console.log(e);
            }
        }
//        log.info(builder.toString());
        service.save(tmpTableName,id,builder.toString());

        builder.clear();
    }



}
